package asm;

import org.objectweb.asm.*;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;

import java.util.List;

import static org.objectweb.asm.Opcodes.ASM5;

public class F_modifyMethod {
    public void modifyMethodByCoreAPI() throws Exception {
        ClassReader cr = Tool.getClassReader();
        ClassWriter cw = new ClassWriter(0);

        ClassVisitor cv = new ClassVisitor(ASM5, cw) {


            @Override
            public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
                if (name.equals("test01")) {
                    MethodVisitor mn = cv.visitMethod(Opcodes.ACC_PUBLIC, name, "(I)I", null, exceptions);
                    mn.visitCode();
                    mn.visitVarInsn(Opcodes.ALOAD, 0);
                    mn.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "com/Application", "test02", "()V", false);
                    mn.visitVarInsn(Opcodes.ALOAD, 0);
                    mn.visitFieldInsn(Opcodes.GETFIELD, "com/Application", "a", "I");
                    mn.visitVarInsn(Opcodes.ALOAD, 0);
                    mn.visitFieldInsn(Opcodes.GETFIELD, "com/Application", "b", "I");
                    mn.visitInsn(Opcodes.IADD);
                    mn.visitVarInsn(Opcodes.ILOAD, 1);
                    mn.visitInsn(Opcodes.IADD);
                    mn.visitInsn(Opcodes.IRETURN);
                    mn.visitMaxs(2, 2);
                    mn.visitEnd();

                    return mn;
                }
                return super.visitMethod(access, name, descriptor, signature, exceptions);
            }
        };
        //cr.accept(cv,ClassReader.SKIP_CODE|ClassReader.SKIP_DEBUG);
        cr.accept(cv, 0);
        byte[] bytesModifield = cw.toByteArray();

        Tool.save(this.getClass(), Application.class, bytesModifield);

    }

    public void modifyMethodByTreeAPI() throws Exception {
        ClassReader cr = Tool.getClassReader();
        ClassNode cn = new ClassNode();
        cr.accept(cn, ASM5);

        //new 方法
        MethodNode newmn = new MethodNode(Opcodes.ACC_PUBLIC, "test01", "(I)I", null, null);
        newmn.visitCode();
        newmn.visitVarInsn(Opcodes.ALOAD, 0);
        newmn.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "com/Application", "test02", "()V", false);
        newmn.visitVarInsn(Opcodes.ALOAD, 0);
        newmn.visitFieldInsn(Opcodes.GETFIELD, "com/Application", "a", "I");
        newmn.visitVarInsn(Opcodes.ALOAD, 0);
        newmn.visitFieldInsn(Opcodes.GETFIELD, "com/Application", "b", "I");
        newmn.visitInsn(Opcodes.IADD);
        newmn.visitVarInsn(Opcodes.ILOAD, 1);
        newmn.visitInsn(Opcodes.IADD);
        newmn.visitInsn(Opcodes.IRETURN);
        newmn.visitMaxs(2, 2);
        newmn.visitEnd();

        // 替换
        List<MethodNode> methods = cn.methods;
        for (int i = 0; i < methods.size(); i++) {
            MethodNode mn = methods.get(i);
            if ("test01".equals(mn.name)) {
                methods.set(i,newmn);
                break;
            }

        }


        // 打印
        ClassWriter cw = new ClassWriter(0);
        cn.accept(cw);
        byte[] bytesModified = cw.toByteArray();
        Tool.save(this.getClass(), Application.class, bytesModified);
    }

    public static void main(String[] args) throws Exception {
        new F_modifyMethod().modifyMethodByTreeAPI();
    }
}
